#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define  EOS    0
#define  SEXP   "( a ( b \"1 2 3\"@01) \"123\"@01 sdf334 )"

static char *source;
static char *pointer;

struct pair {
    struct sexp *car;
    struct sexp *cdr;
};

struct sexp {
    int type;
    union {
        char* a;
        struct pair *p;
    };
};


void eat_comments() {
    while ((*pointer != EOS) && (*pointer != '\n')) {
        pointer++;
    }
}

void eat_whitespace() {
    while (*pointer != EOS) {
        if (isspace(*pointer)) {
            pointer++;
            continue;
        }
        if (*pointer == ';') { /* comments are whitespace also */
            eat_comments();
            continue;
        }
        else {
            break;
        }
    }
}

int is_delimiter(int c) {
    return isspace(c) || c == EOF ||
           c == '('   || c == ')' ||
           c == '"'   || c == ';';
}

int is_digit(int c) {
    return ((char)c) >= '0' && ((char)c) <= '9';
}

int digit_value(int c) {
    return c - ((int) '0');
}

int read_enum() {
    int r = 0;
    while(is_digit(*pointer)) {
        r = r << 4 + digit_value(*pointer);
        pointer++;
    }
    return r;
}

char* read_string() {
    int i = 0;
    char *buffer = (char *)malloc(1024 * sizeof(char));

    pointer++;
    while (*pointer != '"') {
        if (*pointer == '\\') {
            pointer++;
            if (*pointer == 'n') {
                *buffer++ = '\n'; // c=\t, c=\\, c=\" ...
            }
        }
        if (*pointer == EOS) {
            printf("Error while read atom!");
        }
        if (i < 1024 - 1) {
            buffer[i++] = *pointer;
        }
        else {
            printf("Error while read atom!");
        }
        pointer++;
    }
    pointer++;
    buffer[i] = '\0';
    return buffer;
}

char* read_operator() {
    int i = 0;
    char *buffer = (char *)malloc(1024 * sizeof(char));
    while (!is_delimiter(*pointer)) {
        if (*pointer == EOS) {
            printf("Error while read operator!");
        }
        if (i < 1024 - 1) {
            buffer[i++] = *pointer;
        }
        else {
            printf("Error while read operator!");
        }
        pointer++;
    }
    buffer[i] = '\0';
    return buffer;
}

char* read_literal() {
    char* lex_string = read_string();
    if (*pointer != '@') {
        printf("Error while read literal!");
    }
    pointer++;
    int type = read_enum();
    return lex_string;
    //return lex(type, lex_string);
}

void *token() {
    eat_whitespace();
    if (*pointer == EOS) {
        return 0;
    } else if (*pointer == '(') {
        pointer++;
        return "(";
    } else if (*pointer == ')') {
        pointer++;
        return ")";
    } else if (*pointer == '.') {
        pointer++;
        return ".";
    } else if (*pointer == '"') {
        return read_literal();
    } else {
        return read_operator();
    }
}

void *peek_token() {
    char *token_pointer = pointer;
    void* t = token();
    pointer = token_pointer;
    return t;
}

void *list();

void *datum() {
    eat_whitespace();
    if (*pointer == '(') {
        return list();
    } else {
        struct sexp *t = (struct sexp *)malloc(sizeof(struct sexp));
        t->type = 1;
        t->a = token();
        return t;
    }
}

void* single_list(void* d) {
    struct sexp *nil = (struct sexp *)malloc(sizeof(struct sexp));
    nil->type = -1;
    nil->p = 0;

    struct sexp *lst = (struct sexp *)malloc(sizeof(struct sexp));
    lst->type = 16;
    lst->p = (struct pair *)malloc(sizeof(struct pair));
    lst->p->car = d;
    lst->p->cdr = nil;
    return lst;
}

void *list() {
    char *lparen = 0;
    char *rparen = 0;
    char *next = 0;
    struct sexp *nil = (struct sexp *)malloc(sizeof(struct sexp));
    struct sexp *node = 0;
    char c = 0;
    void *d = 0;
    void *e = 0;
    void *lst = 0;
    struct sexp *the_cdr = 0;
    nil->type = -1;
    nil->p = 0;

    eat_whitespace();
    lparen = token();
    if (lparen && *lparen != '(') {
        printf("error!");
    }
    next = peek_token();
    if (next && *next == ')') {
        token();
        return nil;
    }
    else {
        d = datum();
        lst = single_list(d);
        the_cdr = lst;
        while (1) {
            eat_whitespace();
            c = *pointer;
            if (c == ')' || c == '.') {
                break;
            }
            e = datum();
            the_cdr->p->cdr = single_list(e);
            the_cdr = the_cdr->p->cdr;
        }
        if (c == '.') {
            token(); //pointer++;
            the_cdr->p->cdr = datum();
        }
        rparen = token();
        if (rparen && *rparen != ')') {
            printf("error!");
        }
        return lst;
    }
}

void print_sexp(struct sexp *exp) {
    struct sexp *t = exp;

    if (exp->type == 1) {
        printf("%s", exp->a);
    } else if  (exp->type == 16) {
        printf("( ");
        while (1) {
            if (t->type != 16) break;
            if (t->p->cdr->type == -1) break;
            if (t->type == 16) {
                print_sexp(t->p->car);
            }
            else {
                print_sexp(t);
            }

            if (t->type == 16) {
                if (t->p->cdr->type != 16) {
                    printf(" . ");
                } else {
                    printf(" ");
                }
            }
            t = t->p->cdr;
        }
        if (t->type == 16) {
            print_sexp(t->p->car);
        }
        else {
            print_sexp(t);
        }
        
//        print_sexp(exp->p->car);
//        printf(" . ");
//        print_sexp(exp->p->cdr);

        printf(" )");
    } else if  (exp->type == -1) {
        printf("()");
    } else {
        perror("error!");
    }
}


int main() {
    //$a0  contains file_name

    source = (char *)malloc(1024 * sizeof(char));
//    strcpy(source, "( ");
//    source = strcat(source, SEXP);
//    source = strcat(source, " )");

    strcpy(source, SEXP);
    pointer = source;

    void* result = list();
    print_sexp(result);
    return 0;
}
